import * as THREE from "three"
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { TextGeometry } from "three/examples/jsm/geometries/TextGeometry";
import { FontLoader } from "three/examples/jsm/loaders/FontLoader";
interface initGeometryOption {
    geometryOption: {
        name: string,
        sizes?: number[],
        option?: Array<any> | any,
        vertices?: Array<number>,
        content?: string
    },
    materialOption: {
        name: string,
        color?: number | string,
        opacity?: number,
        transparent?: boolean,
        specular?: string,
        shininess?: number,
        size?: number,
        side?: any,
        wireframe?: any
    },
    objectName?: string,
    beforeAdd?: Function | null
}

class Box {
    private scene;
    private camera;
    private renderer;
    private container;

    constructor() { }
    init(container) {
        this.container = container;

        // 初始化场景
        this.setScene();
        // 设置相机    
        this.setCamera();
        // 设置渲染器
        this.setRender();
        // 启动渲染
        this.render();
        // 动作监听
        this.addControl();
        //监听页面缩放
        this.addListenScale();
    }
    // 场景
    setScene() {
        const that = this;

        this.scene = new THREE.Scene();
        // this.scene.background = new THREE.Color("#1A1A1A");背景色
        // this.scene.fog = new THREE.Fog(0x1A1A1A, 1, 1000);雾化

        //创建立方体模型
        this.createGeometry({
            geometryOption: {
                name: "BoxGeometry",
                sizes: [100, 100, 100],
            },
            materialOption: {
                name: "MeshLambertMaterial",    //漫反射
                color: 0x0000ff
            },
            beforeAdd(mesh) {
                mesh.position.set(-150, 0, 0);//设置mesh模型对象的xyz坐标为120,0,0
            }
        });
        //创建球体模型
        this.createGeometry({
            geometryOption: {
                name: "SphereGeometry",
                sizes: [60, 40, 40],
            },
            materialOption: {
                name: "MeshLambertMaterial",    //漫反射
                color: 0x0000ff,
                opacity: 0.7,
                transparent: true
            },
            beforeAdd(mesh) {
                mesh.translateY(120); //球体网格模型沿Y轴正方向平移120
            }
        });
        //创建圆柱体模型
        this.createGeometry({
            geometryOption: {
                name: "CylinderGeometry",
                sizes: [50, 50, 100, 25],
            },
            materialOption: {
                name: "MeshPhongMaterial",  //镜面反射：添加高光效果(高光白色)
                color: 0xffff00,
                specular: "#ffffff",        //高光颜色
                shininess: 12               //强度系数
            },
            beforeAdd(mesh) {
                mesh.position.set(120, 0, 0);//设置mesh模型对象的xyz坐标为120,0,0
            }
        });
        //创建文字
        const loader = new FontLoader();
        loader.load('/fonts/helvetiker_regular.typeface.json', function (font) {
            that.createGeometry({
                geometryOption: {
                    name: "TextGeometry",
                    content: 'Hello three.js!',
                    option: {
                        font: font,
                        size: 80,
                        height: 5,
                        curveSegments: 12,
                        bevelEnabled: true,
                        bevelThickness: 10,
                        bevelSize: 8,
                        bevelSegments: 5
                    }
                },
                materialOption: {
                    name: "MeshPhongMaterial",  //镜面反射：添加高光效果(高光白色)
                    color: 0xffff00,
                    specular: "#ffffff",        //高光颜色
                    shininess: 12               //强度系数
                },
                beforeAdd(mesh) {
                    mesh.position.set(-200, 0, -100);//设置mesh模型对象的xyz坐标为120,0,0
                }
            });

        });

        //创建一个平面
        let shape1 = new THREE.Shape([
            new THREE.Vector2(0, 0),
            new THREE.Vector2(60, 0),
            new THREE.Vector2(60, 80),
            new THREE.Vector2(40, 120),
            new THREE.Vector2(-20, 80),
        ]);
        let shape2 = new THREE.Shape([
            new THREE.Vector2(100, 0),
            new THREE.Vector2(160, 0),
            new THREE.Vector2(100, 80)
        ]);
        this.createGeometry({
            geometryOption: {
                name: "ShapeGeometry",
                option: [shape1, shape2],
            },
            materialOption: {
                name: "MeshLambertMaterial",  //漫反射
                color: "#00ffff",
                // wireframe: true,        //查看生成的三角形
                side: THREE.DoubleSide      //双面显示
            },
            beforeAdd(mesh) {
                mesh.position.set(200, 0, 0);//设置mesh模型对象的xyz坐标为120,0,0
            }
        });

        var vertices = [
            0, 0, 0, 	//顶点1坐标
            50, 0, 0, 	//顶点2坐标
            0, 100, 0, 	//顶点3坐标
            0, 0, 10, 	//顶点4坐标
            0, 0, 100, 	//顶点5坐标
            50, 0, 10, 	//顶点6坐标
        ];
        // 创建点
        this.createGeometry({
            geometryOption: {
                name: "BufferGeometry",
                vertices: vertices
            },
            materialOption: {
                name: "PointsMaterial",
                color: "#ff0000",
                size: 10
            },
            objectName: "Points",
            beforeAdd(mesh) {
                mesh.position.set(0, 0, 100);//设置mesh模型对象的xyz坐标为120,0,0
            }
        });

        // 创建面
        this.createGeometry({
            geometryOption: {
                name: "BufferGeometry",
                vertices: vertices
            },
            materialOption: {
                name: "MeshPhongMaterial",
                color: "#00ff00",
                side: THREE.DoubleSide
            },
            beforeAdd(mesh) {
                mesh.position.set(100, 0, 100);//设置mesh模型对象的xyz坐标为120,0,0
            }
        });

        // //创建线
        this.createGeometry({
            geometryOption: {
                name: "BufferGeometry",
                vertices: vertices
            },
            materialOption: {
                name: "LineBasicMaterial",
                color: "#ff0000"
            },
            objectName: "Line",
            beforeAdd(mesh) {
                mesh.position.set(200, 0, 100);//设置mesh模型对象的xyz坐标为120,0,0
            }
        });

        const object = new THREE.Object3D();
        this.scene.add(object);

        // 绘制棱锥
        this.createCone();
        //贴图
        this.createSign();

        //自定义围墙
        this.createFence();

        //点光源
        this.createLight("PointLight", (point) => point.position.set(200, 300, 400), "#ffffff", 0.7);

        // 点光源2  位置和point关于原点对称
        this.createLight("PointLight", (point) => point.position.set(-400, -200, -300), "#ffffff");

        //环境光
        this.createLight("AmbientLight", () => { }, "#444444");

        // 创建坐标系
        this.createAxis();
    }

    //创建围墙
    createFence() {
        var c = [
            0, 0, //顶点1坐标
            60, 0, //顶点2坐标
            60, 80, //顶点3坐标
            40, 120, //顶点4坐标
            -20, 80, //顶点5坐标
            0, 0, //顶点6坐标  和顶点1重合
        ]

        var geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
        var posArr = [];
        var h = 20; //围墙拉伸高度
        for (var i = 0; i < c.length - 2; i += 2) {
            for (var i = 0; i < c.length - 2; i += 2) {
                //这里画了两个三角形，组成了一个矩形
                // 三角形1  三个顶点坐标
                posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], 0, c[i + 2], c[i + 3], h);
                // 三角形2  三个顶点坐标
                posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], h, c[i], c[i + 1], h);
            }
        }
        // 设置几何体attributes属性的位置position属性
        geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(posArr), 3);
        geometry.computeVertexNormals();
        var material = new THREE.MeshPhongMaterial({
            color: "#0000ff",
            side: THREE.DoubleSide,
            // wireframe: true
        });
        let mesh = new THREE.Mesh(geometry, material);
        mesh.position.set(0, 0, 210);
        this.scene.add(mesh);
    }
    // 绘制棱锥
    createCone() {
        var height = 100;//棱锥高度
        var radius = 25;//半径
        // 圆锥体几何体API(ConeGeometry)圆周方向四等分实现四棱锥效果
        var geometry = new THREE.ConeGeometry(radius, height, 4);
        // 缓冲类型几何体BufferGeometry没有computeFlatVertexNormals方法
        // (geometry as any).computeFlatVertexNormals();//一种计算顶点法线方式，非光滑渲染
        // 可以根据需要旋转到特定角度
        // geometry.rotateX(Math.PI);
        geometry.rotateX(-Math.PI / 2);
        geometry.translate(0, 0, height / 2);
        // MeshBasicMaterial MeshLambertMaterial
        var material = new THREE.MeshLambertMaterial({
            color: 0x00ffff,
        });
        var mesh = new THREE.Mesh(geometry, material);
        this.scene.add(mesh);
    }

    // 打上标记
    createSign() {
        var geometry = new (THREE as any).PlaneBufferGeometry(60, 60); //默认在XOY平面上
        var textureLoader = new THREE.TextureLoader(); // TextureLoader创建一个纹理加载器对象
        var material = new THREE.MeshBasicMaterial({
            map: textureLoader.load('/images/贴图.png'),
            transparent: true, //使用背景透明的png贴图，注意开启透明计算
            // side: THREE.DoubleSide, //双面可见
        });
        var mesh = new THREE.Mesh(geometry, material);
        mesh.rotateX(-Math.PI / 2); //旋转到XOZ平面
        this.scene.add(mesh);
    }

    // 相机
    setCamera() {
        var width = this.container.offsetWidth; //窗口宽度
        var height = this.container.offsetHeight; //窗口高度
        var k = width / height; //窗口宽高比
        var s = 200; //三维场景显示范围控制系数，系数越大，显示的范围越大
        //创建相机对象
        this.camera = new THREE.OrthographicCamera(-s * k, s * k, s, -s, 1, 1000);
        this.camera.position.set(200, 300, 200); //设置相机位置
        this.camera.lookAt(this.scene.position); //设置相机方向(指向的场景对象)
    }
    // 渲染器
    setRender() {
        this.renderer = new THREE.WebGLRenderer();
        this.renderer.setSize(this.container.offsetWidth, this.container.offsetHeight);//设置渲染区域尺寸
        this.renderer.setClearColor("#333333", 1); //设置背景颜色

        this.container.appendChild(this.renderer.domElement); //body元素中插入canvas对象
    }
    // 渲染
    render() {
        this.renderer.render(this.scene, this.camera);

        // 自转
        this.scene.children[0].rotation.y += .02

        //监听鼠标、键盘事件
        requestAnimationFrame(this.render.bind(this));
    }
    // 鼠标和键盘事件
    addControl() {
        var controls = new OrbitControls(this.camera, this.renderer.domElement);//创建控件对象
        controls.enableDamping = true;  //开启控制器的移动惯性，这样在使用鼠标交互过程中就会感觉更加流畅和逼真。
    }
    // 创建网格模型
    createGeometry(option: initGeometryOption) {
        let { geometryOption: { name: geometryName, ...gOption }, materialOption: { name: materialName, ...mOption }, objectName, beforeAdd } = option;
        var geometry = null;
        if (geometryName === "ShapeGeometry") {
            geometry = new THREE["ShapeGeometry"](gOption.option);
        } else if (geometryName === "BufferGeometry") {
            //创建一个Buffer类型几何体对象
            geometry = new THREE.BufferGeometry();
            //类型数组创建顶点数据
            var vertices = new Float32Array(gOption.vertices);
            // 创建属性缓冲区对象
            var attribue = new THREE.BufferAttribute(vertices, 3); //3个为一组，表示一个顶点的xyz坐标
            // 设置几何体attributes属性的位置属性
            geometry.attributes.position = attribue;
            geometry.computeVertexNormals();
        } else if (geometryName === "TextGeometry") {
            geometry = new TextGeometry(gOption.content, gOption.option);
        } else {
            geometry = new THREE[geometryName](...gOption.sizes);
        }
        var material = new THREE[materialName](mOption);
        var mesh = new THREE[objectName || "Mesh"](geometry, material); //网格模型对象Mesh
        beforeAdd && beforeAdd(mesh);
        this.scene.add(mesh); //网格模型添加到场景中
    }
    // 创建光源
    createLight(name, beforeAdd = null, ...options) {
        var point = new THREE[name](...options);
        beforeAdd && beforeAdd(point);
        this.scene.add(point); //点光源添加到场景中
    }
    // 创建坐标系
    createAxis() {
        //辅助坐标系  参数250表示坐标系大小，可以根据场景大小去设置
        var axisHelper = new THREE.AxesHelper(250);
        this.scene.add(axisHelper);
    }
    // 页面缩放事件监听
    addListenScale() {
        window.addEventListener('resize', () => {
            let sizes = {
                width: 0,
                height: 0
            };
            sizes.width = this.container.offsetWidth;
            sizes.height = this.container.offsetHeight;
            // 更新渲染
            this.renderer.setSize(sizes.width, sizes.height);
            this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
            // 更新相机
            this.camera.aspect = sizes.width / sizes.height;
            this.camera.updateProjectionMatrix();
        });
    }


}

export default Box;